#!/usr/bin/env perl

use warnings;

use strict;

use English;
use FindBin;
use lib "$FindBin::Bin/../../lib";

use Getopt::Long;
use MusicBrainz::Script::Utils qw(
    copy_table_from_file
    find_mbdump_file
    is_table_empty
);
use MusicBrainz::Server::Context;
use DBDefs;
use Sql;

my ($fHelp, $fIgnoreErrors);
my $tmpdir = '/tmp';
my $database = 'READWRITE';
my $dbmirror2 = 1;

GetOptions(
    'help|h'            => \$fHelp,
    'database=s'        => \$database,
    'dbmirror2!'        => \$dbmirror2,
    'ignore-errors|i!'  => \$fIgnoreErrors,
    'tmp-dir|t=s'       => \$tmpdir,
);

sub usage
{
    print <<EOF;
Usage: ImportReplicationChanges [options] FILE ...

        --help            show this help
        --database        database to import into (default: READWRITE)
    -i, --ignore-errors   if a table fails to import, continue anyway
    -t, --tmp-dir DIR     use DIR for temporary storage (default: /tmp)

FILE can be any of: a regular file in Postgres "copy" format (as produced
by ExportReplicationChanges --nocompress); a gzip'd or bzip2'd tar file of Postgres
"copy" files (as produced by ExportReplicationChanges); a directory containing
Postgres "copy" files; or a directory containing an "mbdump" directory
containing Postgres "copy" files.

If any "tar" files are named, they are firstly all
decompressed to temporary directories (under the directory named by
--tmp-dir).  These directories are removed on exit.

EOF
}

$fHelp and usage();
@ARGV or usage();

$SIG{'INT'} = sub { die "SIGINT\n" };

my $c = MusicBrainz::Server::Context->create_script_context(database => $database);
my $sql = Sql->new($c->conn);

for my $arg (@ARGV)
{
    -e $arg or die "'$arg' not found";
    next if -d _;
    -f _ or die "'$arg' is neither a regular file nor a directory";

    next unless $arg =~ /\.tar\.(gz|bz2)$/;

    my $mode = ($1 eq 'gz' ? 'gzip' : 'bzip2');

    my $dir = make_tmp_dir();
    print localtime() . " : tar -C $dir --$mode -xvf $arg\n";
    system "tar -C $dir --$mode -xvf $arg";
    exit $CHILD_ERROR if $CHILD_ERROR;
    $arg = $dir;
}

use Time::HiRes qw( gettimeofday tv_interval );
my $t0 = [gettimeofday];
my $totalrows = 0;
my $tables = 0;
my $errors = 0;

print localtime() . " : starting import\n";

printf "%-30.30s %9s %4s %9s\n",
    'Table', 'Rows', 'est%', 'rows/sec',
    ;

if ($dbmirror2) {
    ImportDBMirror2ReplicationTables();
} else {
    ImportReplicationTables();
}

print localtime() . " : import finished\n";

my $dumptime = tv_interval($t0);
printf "Loaded %d tables (%d rows) in %d seconds\n",
    $tables, $totalrows, $dumptime;

exit($errors ? 1 : 0);

sub ImportTable
{
    my ($table, $file) = @_;

    my $rows = copy_table_from_file(
        $sql, $table, $file,
        ignore_errors => $fIgnoreErrors,
    );

    if ($rows) {
        ++$tables;
        $totalrows += $rows;
        return 1;
    } else {
        ++$errors;
        return 0;
    }
}

sub ImportReplicationTables
{
    $sql->auto_commit;
    eval { $sql->do('ALTER TABLE dbmirror_pendingdata DROP CONSTRAINT "dbmirror_pendingdata_SeqId" CASCADE') };
    $sql->auto_commit;
    eval { $sql->do('ALTER TABLE dbmirror_pending DROP CONSTRAINT "dbmirror_pending_pkey" CASCADE') };
    $sql->auto_commit;
    eval { $sql->do('ALTER TABLE dbmirror_pendingdata DROP CONSTRAINT "dbmirror_pendingdata_pkey" CASCADE') };

    for my $table (('dbmirror_pending', 'dbmirror_pendingdata'))
    {
        my $file = find_mbdump_file($table, @ARGV);
        $file or print("No data file found for '$table', skipping\n"), die;

        if (not is_table_empty($sql, $table))
        {
            die "$table already contains data; skipping\n";
            next;
        }

        ImportTable($table, $file);
    }

    $sql->begin;
    $sql->do('ALTER TABLE dbmirror_pending ADD CONSTRAINT dbmirror_pending_pkey PRIMARY KEY (SeqId)');
    $sql->do('ALTER TABLE dbmirror_pendingdata ADD CONSTRAINT dbmirror_pendingdata_pkey PRIMARY KEY (SeqId, IsKey)');
    $sql->do('ALTER TABLE dbmirror_pendingdata ADD CONSTRAINT dbmirror_pendingdata_SeqId FOREIGN KEY (SeqId) REFERENCES dbmirror_pending (SeqId) ON UPDATE CASCADE ON DELETE CASCADE');
    $sql->commit;

    return 1;
}

sub ImportDBMirror2ReplicationTables {
    for my $table (qw( pending_data pending_keys pending_ts )) {
        my $file = find_mbdump_file($table, @ARGV);

        if (!$file) {
            # See NOTE-NOPK-1 in LoadReplicationChanges.
            next if $table eq 'pending_keys';
            die "No data file found for $table";
        }

        my $qualified_table = "dbmirror2.$table";
        if (!is_table_empty($sql, $qualified_table)) {
            die "$qualified_table already contains data";
        }

        ImportTable($qualified_table, $file);
    }

    return 1;
}

{
    my @tmpdirs;

    END
    {
        use File::Path;
        rmtree(\@tmpdirs);
    }

    sub make_tmp_dir
    {
        for (my $i = 0; ; ++$i)
        {
                my $dir = "$tmpdir/ImportRepl-$$-$i";

                if (mkdir $dir)
                {
                        push @tmpdirs, $dir;
                        return $dir;
                }

                use Errno 'EEXIST';
                next if $OS_ERROR == EEXIST;

                die "Error creating temporary directory ($OS_ERROR)";
        }
    }
}

=head1 COPYRIGHT AND LICENSE

Copyright (C) 1998 Robert Kaye

This file is part of MusicBrainz, the open internet music database,
and is licensed under the GPL version 2, or (at your option) any
later version: http://www.gnu.org/licenses/gpl-2.0.txt

=cut
